namespace ClassyBlog.Infrastructure.Specs.Unit
{
    using System;
    using System.Web;
    using System.Web.Mvc;

    using Machine.Specifications;
    using NSubstitute;

    using DomainObjects;
    using Infrastructure;
    using Web;

    [Subject(typeof(CurrentBlog))]
    public class when_creating_current_blog_with_null_current_blog_provider
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new CurrentBlog(null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(CurrentBlog))]
    public class when_current_blog_is_created
    {
        static CurrentBlog filter;

        Because of = () => filter =
            new CurrentBlog(Substitute.For<ICurrentBlogProvider>());

        It should_not_allow_multiple_instance = () =>
            filter.AllowMultiple.ShouldBeFalse();
    }

    public abstract class CurrentBlogSpec
    {
        protected static HttpContextBase httpContext;
        protected static Blog blog;
        protected static ICurrentBlogProvider provider;
        protected static CurrentBlog filter;

        Establish context = () =>
        {
            httpContext = Substitute.For<HttpContextBase>();

            blog = new Blog();
            provider = Substitute.For<ICurrentBlogProvider>();

            filter = new CurrentBlog(provider);
        };
    }

    [Subject(typeof(CurrentBlog))]
    public class when_executing_current_blog_with_null_context :
        CurrentBlogSpec
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => filter.OnActionExecuting(null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(CurrentBlog))]
    public class when_current_blog_is_not_set_for_controller :
        CurrentBlogSpec
    {
        static Controller controller;

        Establish context = () =>
        {
            provider.Current.Returns(blog);
            controller = Substitute.For<Controller, ICurrentBlogAware>();
        };

        Because of = () =>
        {
            var context = new ActionExecutingContext
                          {
                                Controller = controller,
                                HttpContext = httpContext
                          };

            filter.OnActionExecuting(context);
        };

        It should_set_current_blog = () =>
            ((ICurrentBlogAware)controller).Received().SetBlog(blog);
    }

    [Subject(typeof(CurrentBlog))]
    public class when_current_blog_is_set_for_controller : CurrentBlogSpec
    {
        static Controller controller;

        Establish context = () =>
        {
            provider.Current.Returns(blog);
            controller = Substitute.For<Controller, ICurrentBlogAware>();
            ((ICurrentBlogAware)controller).Blog.Returns(blog);
        };

        Because of = () =>
        {
            var context = new ActionExecutingContext
                          {
                                Controller = controller,
                                HttpContext = httpContext
                          };

            filter.OnActionExecuting(context);
        };

        It should_not_set_current_blog = () =>
            ((ICurrentBlogAware)controller).DidNotReceive().SetBlog(blog);
    }

    [Subject(typeof(CurrentBlog))]
    public class when_executed_current_blog_with_null_context : CurrentBlogSpec
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => filter.OnActionExecuted(null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(CurrentBlog))]
    public class when_current_blog_is_not_set_for_view_model : CurrentBlogSpec
    {
        static Controller controller;
        static ViewModel viewModel;

        Establish context = () =>
        {
            viewModel = Substitute.For<ViewModel>();

            controller = Substitute.For<Controller>();
            controller.ViewData.Model = viewModel;

            provider.Current.Returns(blog);
        };

        Because of = () =>
        {
            var context = new ActionExecutedContext
                          {
                                Controller = controller,
                                HttpContext = httpContext
                          };

            filter.OnActionExecuted(context);
        };

        It should_set_current_blog = () =>
            ((ICurrentBlogAware)viewModel).Received().SetBlog(blog);
    }

    [Subject(typeof(CurrentBlog))]
    public class when_current_blog_is_set_for_view_model : CurrentBlogSpec
    {
        static Controller controller;
        static ViewModel viewModel;

        Establish context = () =>
        {
            viewModel = Substitute.For<ViewModel>();
            viewModel.Blog.Returns(blog);

            controller = Substitute.For<Controller>();
            controller.ViewData.Model = viewModel;

            provider.Current.Returns(blog);
        };

        Because of = () =>
        {
            var context = new ActionExecutedContext
                          {
                                Controller = controller,
                                HttpContext = httpContext
                          };

            filter.OnActionExecuted(context);
        };

        It should_not_set_current_blog = () =>
            ((ICurrentBlogAware)viewModel).DidNotReceive().SetBlog(blog);
    }
}